
package hasthi.common;

import static java.util.regex.Pattern.compile;
import hasthi.actuators.CreateServiceAction;
import hasthi.actuators.ManagementAction;
import hasthi.common.HasthiException.FaultCode;
import hasthi.common.constants.HasthiConstants;
import hasthi.common.constants.WSDMConstants;
import hasthi.manager.ManagerAddress;
import hasthi.manager.ManagerContext;
import hasthi.metamodel.resource.ManagedResource;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.net.URLDecoder;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.StringTokenizer;
import java.util.UUID;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.xml.namespace.QName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xmlpull.mxp1_serializer.MXSerializer;
import org.xmlpull.v1.builder.XmlElement;

import xsul.XmlConstants;

public class Utils {
    private final static String PROPERTY_SERIALIZER_INDENTATION = "http://xmlpull.org/v1/doc/properties.html#serializer-indentation";

    private final static String INDENT = "    ";
    private static Random random = new Random(System.currentTimeMillis() + UUID.randomUUID().hashCode());
    private static String localHostName;
    private static Pattern restReqPattern = Pattern.compile(".*hasthi/(.*?)/(.*?)");
    
    private static String masteruuid = UUID.randomUUID().toString();
    private static long uuidCount = 0;
    
    
    private static Log log = LogFactory.getLog(HasthiConstants.LOGGER_NAME);
    public static Class loadClass(String className) throws ClassNotFoundException{
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        return classLoader.loadClass(className);
    }
    
    

    public static String readFromStream(InputStream in) throws IOException {
        StringBuffer wsdlStr = new StringBuffer();

        int read;

        byte[] buf = new byte[1024];
        while ((read = in.read(buf)) > 0) {
            wsdlStr.append(new String(buf, 0, read));
        }
        in.close();
        return wsdlStr.toString();
    }
    
    public static void readAndWriteFileToOutputStream(File file,OutputStream out) throws IOException {
        FileInputStream in = new FileInputStream(file);
        int read;
        byte[] buf = new byte[1024];
        while ((read = in.read(buf)) > 0) {
            out.write(buf,0,read);
        }
        in.close();
    }
    
    
    
    

    public static String readFile(String file) throws IOException {
        FileInputStream in = new FileInputStream(file);
        return readFromStream(in);
    }

    public static String prettyPrint2String(XmlElement ele) {
        if(ele == null){
            return "null";
        }
        MXSerializer serializer = new MXSerializer();
        StringWriter writer = new StringWriter();
        serializer.setOutput(writer);
        serializer.setProperty(PROPERTY_SERIALIZER_INDENTATION, INDENT);
        XmlConstants.BUILDER.serialize(ele, serializer);
        return writer.toString();
    }

    public static void prettyPrint(XmlElement ele)  {
         System.out.println(prettyPrint2String(ele));
    }
    
    
    public static QName uri2QName(String uri){
        int splitterIndex = uri.lastIndexOf('/');
        if(splitterIndex >= 0){
            return new QName(uri.substring(0, splitterIndex),uri.substring(splitterIndex+1));
        }else{
            return new QName(uri);
        }
    }
    
    public static String mngAdr2Id(ManagerAddress address){
        return address.getSoapAddress();
    }
    
    public static boolean equals(ManagerAddress address1,ManagerAddress address2){
        if(address1 == null || address2 == null){
            return false;
        }else{
            return address1.getSoapAddress().equals(address2.getSoapAddress());
        }
    }


    public static double nextGaussian(){
        return random.nextGaussian();
    }
    

    public static boolean selectWithProbability(double p) {
        int randomInt = random.nextInt(100000000);
        if(randomInt < 100000000*p){
            return true;
        }else{
            return false;
        }
    }
    
    public static void verifyListIsSorted(ManagerAddress[] addresses){
        for(int i = 1;i<addresses.length;i++){
            if(addresses[i-1].getRank() == 0 || addresses[i].getRank() == 0){
                throw new HasthiError("Manager addressed is not initialized");
            }
            if(addresses[i-1].getRank() > addresses[i].getRank()){
                throw new HasthiError("Manager addressed must be sorted"+ Arrays.toString(addresses));
            }
        }
    }
    
    public static boolean isBetterCoordinator(ManagerContext managerContext, int rank){
        return rank < managerContext.getMyRank();
    }
    
    public static long getLongProperty(Properties properties,String name,long defaulVal) {
        String value = properties.getProperty(name);
        return value == null ?defaulVal :Integer.valueOf(value);
    }

    public static int getIntProperty(Properties properties,String name,int defaulVal) {
        String value = properties.getProperty(name);
        return value == null ?defaulVal :Integer.valueOf(value);
    }

    public static String getStringProperty(Properties properties,String name,String defaulVal) {
        String value = properties.getProperty(name);
        return value == null ?defaulVal :value;
    }
    
    private static HashMap<String, QName> propertyName2QName = new HashMap<String, QName>();
    
    static{
        QName[] qnames = new QName[]{new QName(WSDMConstants.MUWS2_NAMESPACE,"Description"),
                new QName(WSDMConstants.MUWS2_NAMESPACE,"Caption"),
                new QName(WSDMConstants.MUWS2_NAMESPACE,"OperationalStatus"),
                new QName(WSDMConstants.MUWS2_NAMESPACE,"CurrentTime"),
                new QName(WSDMConstants.MUWS2_NAMESPACE,"CurrentOperationalState"),
                new QName(WSDMConstants.MUWS2_NAMESPACE,"LastOperationalStateTransition"),
                new QName(WSDMConstants.MUWS2_NAMESPACE,"NumberOfFailedRequests"),
                new QName(WSDMConstants.MUWS2_NAMESPACE,"NumberOfSuccessfulRequests"),
                new QName(WSDMConstants.MUWS2_NAMESPACE,"ServiceTime"),
                new QName(WSDMConstants.MUWS2_NAMESPACE,"MaxResponseTime"),
                new QName(WSDMConstants.MUWS2_NAMESPACE,"LastResponseTime")
        };
        for(QName name:qnames){
            propertyName2QName.put(name.getLocalPart(), name);    
        }
    }
    
    public static QName getPropertyQNameByName(String name){
        QName qname = propertyName2QName.get(name);
        if(qname == null){
            return new QName(WSDMConstants.EXTREME_MANAGEMENT_NS,name);
        }else{
            return qname;
        }
    }
    
    public static String replaceWithVaraibles(String sourceStr,String regex,String replaceStr){
        Pattern p = compile(regex);
        Matcher matcher = p.matcher(sourceStr);
        return matcher.replaceAll(replaceStr);
    }
    
    public static boolean isMatches(String sourceStr,String regex){
        Pattern p = compile(regex);
        Matcher matcher = p.matcher(sourceStr);
        return matcher.matches();
    }
    
    public static final String findMatch(String str,Pattern p){
        Matcher matcher = p.matcher(str);
        while(matcher.find()){
            return matcher.group();
        }
        return null;
    }
    
    
    public static String getLocalHost(){
        try {
            if(localHostName == null){
                localHostName = InetAddress.getLocalHost().getCanonicalHostName();
            }
            return localHostName;
        } catch (UnknownHostException e) {
            return "localhost";
        }
    }
    
    
    public static boolean isServerAvaiable(String url) throws HasthiException{
        try {
            URL tempurl = new URL(url);
            String host = tempurl.getHost();
            int port = tempurl.getPort();
            new Socket(host,port).close();
            return true;
        } catch (MalformedURLException e) {
            throw new HasthiException("Invalied URL "+url,e,FaultCode.InvaliedLocalArgumnet);
        } catch (UnknownHostException e) {
            throw new HasthiException("Invalied Host in URL "+url,e,FaultCode.InvaliedLocalArgumnet);
        } catch (IOException e) {
            return false;
        }
    }
    
    public static int randomInt(int n){
        return Math.abs(random.nextInt(n));
    }
    
    public static String getID(ManagerAddress address){
        StringBuffer buf = new StringBuffer();
        buf.append(address.getSoapAddress()).append("(").append(address.getRank()).append(")");
        return buf.toString();
    }
    
    
    public static String printSucessorList(ManagerAddress[] sucessors){
        StringBuffer buf = new StringBuffer();
        buf.append("[");
        for(ManagerAddress sucessor:sucessors){
            buf.append(sucessor.getRank()).append(" ");
        }
        buf.append("]");
        return buf.toString();
    }
    
    public static String[] parseURL(String requestUrl) throws HasthiException{
        try {
            Matcher matcher = restReqPattern.matcher(requestUrl);
            if(matcher.matches()){
                return new String[]{matcher.group(1),URLDecoder.decode(matcher.group(2),"UTF-8")};
            }
            return null;
        } catch (UnsupportedEncodingException e) {
            throw new HasthiException(e,FaultCode.LocalError);
        }
    }
    
    public static Map<String, String> parseQueryString(String queryString) throws HasthiException{
        if(queryString.startsWith("?")){
            queryString = queryString.substring(1);
        }
        if(!queryString.contains("=")){
            return null;
        }
        Map<String, String> results = new HashMap<String, String>(4);
        StringTokenizer tk = new StringTokenizer(queryString,"&");
        while(tk.hasMoreTokens()){
            String t = tk.nextToken();
            int index = t.indexOf('=');
            if(index > 0){
                results.put(t.substring(0,index), t.substring(index+1));
            }
        }
        return results;
    }
    
    public static QName getTimestampedName(QName name ) {
        Date startTime = new Date();
        String servicePostfix = startTime.toString().replaceAll("([ :\t])+", "_");
        return new QName(name.getNamespaceURI(), name.getLocalPart()+ servicePostfix);
    }

    public boolean isClassAvailable(String className){
        try {
            Class.forName(className);
            return true;
        } catch (ClassNotFoundException e) {
            return false;
        }
    }
    
    public static void append2file(String file,String str){
        try {
            FileWriter w = new FileWriter(file,true);
            w.write(str);
            w.write("\n");
            w.close();
        } catch (IOException e) {
           log.error(e);
        }
    }
    
    public static String errorTraceAsStr(Throwable e){
        StringWriter w = new StringWriter();
        PrintWriter pw = new PrintWriter(w);
        e.printStackTrace(pw);
        pw.close();
        return w.getBuffer().toString();
    }
    
    
    public static String createUUID(){
        StringBuffer buf = new StringBuffer();
        uuidCount++;
        buf.append(masteruuid);
        buf.append(uuidCount);
        return buf.toString();
    }
    
    public static boolean isActiveResource(ManagedResource resource) throws HasthiException{
        return resource != null && (
                resource.getState().equals(WSDMConstants.SystemState.BusyState.toString())
                || resource.getState().equals(WSDMConstants.SystemState.SaturatedState.toString())
                || resource.getState().equals(WSDMConstants.SystemState.IdleState.toString())
                || resource.getState().equals(WSDMConstants.SystemState.UpState.toString())
       );
    }
}

